﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices.AccountManagement;
using MTO.Framework.DirectoryServices.AccountManagement.LDAP;
using System.Text.RegularExpressions;
using MTO.Framework.Common;
using MTO.Framework.DirectoryServices.Helpers;

namespace MTO.Framework.DirectoryServices.AccountManagement
{
    /// <summary>
    /// Encapsule la création d'un LDAPAccountManager avec des propriété propre pour la communication avec un Active Directory Ligthweight Directory Service.
    /// </summary>
    /// <remarks>Pour ce connecter a un Active Directory utilser ADLDSAccountManager.</remarks>
    public class ADLDSAccountManager : LDAPAccountManager
    {
        internal ADLDSAccountManager(string serverName, string container, string creationContainer, ContextOptions contextOptions)
            : base(serverName, container, creationContainer, ContextType.ApplicationDirectory, contextOptions)
        {
        }

        internal ADLDSAccountManager(string serverName, string container, string creationContainer, ContextOptions contextOptions, string userName, string password)
            : base(serverName, container, creationContainer, ContextType.ApplicationDirectory, contextOptions, userName, password)
        {
        }

        /// <summary>
        /// Set up the ADLDSAccountManager to communicate with the given serverName using simple LDAP bind authentication. 
        /// </summary>
        /// <param name="serverName">Name of the server. Please use a fully qualified name (ex : someserver.tourisme.gouv.qc.ca). You can specify the port with :PortNumer (ex : someserver.tourisme.gouv.qc.ca:389)</param>
        /// <param name="container">AD Container that will containt every user allowed to login</param>
        /// <param name="creationContainer">AD Container that will be used to create new users</param>
        /// <param name="useSSL">If set to false, will use default port 389 unless specified in servername.
        /// If set to true, will use default port 636 unless specified in servername</param>
        /// <returns></returns>
        public static ADLDSAccountManager InitializeWithSimpleBind(string serverName, string container, string creationContainer, bool useSSL)
        {
            var options = ContextOptions.SimpleBind;

            if (useSSL)
            {
                options |= ContextOptions.SecureSocketLayer;

                if (!Regex.IsMatch(serverName, ".+:\\d{1,6}"))
                {
                    serverName += ":636"; //Default Directory Services SSL Eanbled Port
                }
            }

            return new ADLDSAccountManager(serverName, container, creationContainer, options);
        }

        /// <summary>
        /// Set up the ADLDSAccountManager to communicate with the given serverName using simple LDAP bind authentication. 
        /// </summary>
        /// <param name="serverName">Name of the server. Please use a fully qualified name (ex : someserver.tourisme.gouv.qc.ca). You can specify the port with :PortNumer (ex : someserver.tourisme.gouv.qc.ca:389)</param>
        /// <param name="container">AD Container that will containt every user allowed to login</param>
        /// <param name="creationContainer">AD Container that will be used to create new users</param>
        /// <param name="useSSL">If set to false, will use default port 389 unless specified in servername.
        /// If set to true, will use default port 636 unless specified in servername</param>
        /// <param name="userName">Username used for impersonalization</param>
        /// <param name="password">Password for the username provided</param>
        /// <returns></returns>
        public static ADLDSAccountManager InitializeWithSimpleBind(string serverName, string container, string creationContainer, bool useSSL, string userName, string password)
        {
            var options = ContextOptions.SimpleBind;

            if (useSSL)
            {
                options |= ContextOptions.SecureSocketLayer;
                if (!Regex.IsMatch(serverName, ".+:\\d{1,6}"))
                {
                    serverName += ":636"; //Default Directory Services SSL Enabled Port
                }
            }

            return new ADLDSAccountManager(serverName, container, creationContainer, options, userName, password);
        }

        public static ADLDSAccountManager InitializeWithSimpleBind(string connectionString, string creationContainer)
        {
            string serverName;
            string container;
            bool useSSL;

            ConnectionHelper.GetInfosFromConnectionString(connectionString, out serverName, out container, out useSSL);

            return InitializeWithSimpleBind(serverName, container, creationContainer, useSSL);
        }

        public static ADLDSAccountManager InitializeWithSimpleBind(string connectionString, string creationContainer, string userName, string password)
        {
            string serverName;
            string container;
            bool useSSL;

            ConnectionHelper.GetInfosFromConnectionString(connectionString, out serverName, out container, out useSSL);

            return InitializeWithSimpleBind(serverName, container, creationContainer, useSSL, userName, password);
        }


        /// <summary>
        /// Set up the ADLDSAccountManager to communicate with the given serverName using Kerberos authentication. 
        /// The thread must have an WindowsPrincipal else it won't be able to connect
        /// </summary>
        /// <param name="serverName">Name of the server</param>
        /// <param name="container">AD Container that will encompass every user allowed to login</param>
        /// <param name="creationContainer">AD Container that will be used to create new users</param>
        /// <param name="useSigning"></param>
        /// <param name="useSealing"></param>
        /// <param name="useServerBind"></param>
        /// <returns></returns>
        public static ADLDSAccountManager InitializeWithKerberos(string serverName, string container, string creationContainer, bool useSigning, bool useSealing, bool useServerBind)
        {
            var options = ContextOptions.Negotiate;

            if (useSigning)
            {
                options |= ContextOptions.Signing;
            }

            if (useServerBind)
            {
                options |= ContextOptions.ServerBind;
            }

            if (useSealing)
            {
                options |= ContextOptions.Sealing;
            }

            return new ADLDSAccountManager(serverName, container, creationContainer, options);

        }

        /// <summary>
        /// Set up the ADLDSAccountManager to communicate with the given serverName using Kerberos authentication. 
        /// The thread must have an WindowsPrincipal else it won't be able to connect.
        /// </summary>
        /// <param name="connectionString">Connexionstring for LDAP server with target container</param>
        /// <param name="creationContainer">AD Container that will be used to create new users</param>
        /// <param name="useSigning"></param>
        /// <param name="useSealing"></param>
        /// <param name="useServerBind"></param>
        /// <returns></returns>
        public static ADLDSAccountManager InitializeWithKerberos(string connectionString, string creationContainer, bool useSigning, bool useSealing, bool useServerBind)
        {

            string serverName;
            string container;
            bool useSSL; //won't be used

            ConnectionHelper.GetInfosFromConnectionString(connectionString, out serverName, out container, out useSSL);

            var options = ContextOptions.Negotiate;

            if (useSigning)
            {
                options |= ContextOptions.Signing;
            }

            if (useServerBind)
            {
                options |= ContextOptions.ServerBind;
            }

            if (useSealing)
            {
                options |= ContextOptions.Sealing;
            }

            return new ADLDSAccountManager(serverName, container, creationContainer, options);
        }
    }
}
